/*
 * @Author: 黄文杰
 * @Date: 2023-06-07 22:26:24
 * @LastEditors: 黄文杰
 * @LastEditTime: 2023-06-14 11:50:27
 * @FilePath: \WDTL40_APP_V1xx\Modbus\modbus.c
 * @Description: MODBUS从机
 *
 * Copyright (c) 2023 by 航信通科技有限公司, All Rights Reserved.
 */
#include "modbus.h"
#include "mbcrc.h"
#include "User_App.h"

uint8_t MB_SendBuf[256]; // 发送缓冲区
uint8_t MB_ReadBuf[256]; // 接收缓冲区

static void Modbus_Send(Modbus_t *modbus, MODBUS_Typedef MB_TmpData);
static void Modbus_DataHandler(Modbus_t *modbus, MODBUS_Typedef MB_TmpData);
void MB_WriteByte(Modbus_t *modbus, unsigned char data);
unsigned int MB_ReadBytes(Modbus_t *modbus, unsigned char *data, unsigned int Datalen);

/**@description: 向buf里写入一个数据
 * @param {Modbus_t} *modbus        要处理的modbus
 * @param {unsigned char} data      写入的数据
 * @return {*} 无
 */
void MB_WriteByte(Modbus_t *modbus, unsigned char data)
{
    if (modbus->MB_Buf.RB_Data == NULL)
    {
        return;
    }

    modbus->MB_Buf.RB_Data[modbus->MB_Buf.RB_Index] = data;
    modbus->MB_Buf.RB_Index++;
    modbus->MB_Buf.timestamp = modbus->GetTimestampus();
    modbus->MB_Buf.BufEmpty = 0;

    if (modbus->MB_Buf.RB_Index >= modbus->MB_Buf.buf_size)
    {
        modbus->MB_Buf.RB_Index = 0;
    }
}

/**@description: 从缓冲区中读取Datalen个数据
 * @param {Modbus_t} *modbus        要处理的modbus
 * @param {unsigned char} *data     返回的数据
 * @param {unsigned char} Datalen   读取的数据长度
 * @return {*} 实际读取的数据个数
 */
unsigned int MB_ReadBytes(Modbus_t *modbus, unsigned char *data, unsigned int Datalen)
{
    for (unsigned int i = 0; i < Datalen; i++)
    {
        if (modbus->MB_Buf.RB_Index != modbus->MB_Buf.RB_Outdex)
        {
            data[i] = modbus->MB_Buf.RB_Data[modbus->MB_Buf.RB_Outdex];
        }
        else
        {
            modbus->MB_Buf.BufEmpty = 1;
            return i;
        }

        modbus->MB_Buf.RB_Outdex++;

        if (modbus->MB_Buf.RB_Outdex >= modbus->MB_Buf.buf_size)
        {
            modbus->MB_Buf.RB_Outdex = 0;
        }
    }

    return Datalen;
}
uint16_t datalen;

/**@description: 数据解析，主函数中调用
 * @param {Modbus_t} *modbus    //要处理的modbus
 * @return {*}  无
 */
void Modbus_Parse(Modbus_t *modbus)
{
    MODBUS_Typedef MB_TmpData;
    // uint16_t datalen;
    datalen = 0;

    if (modbus->MB_Buf.BufEmpty || modbus->GetTimestampus() < modbus->MB_Buf.timestamp + modbus->MB_Timeout)
    {
        return;
    }
    // if (modbus->MB_Buf.BufEmpty)
    // {
    //     return;
    // }

    datalen = MB_ReadBytes(modbus, MB_ReadBuf, sizeof(MB_ReadBuf));

    if (MB_ReadBuf[0] != modbus->Local_add && MB_ReadBuf[0] != 0)
    {
        return;
    }

    if (datalen < 5 || usMBCRC16(MB_ReadBuf, datalen - 2) != ((MB_ReadBuf[datalen - 2] << 8) | MB_ReadBuf[datalen - 1]))
    {
        return;
    }

    MB_TmpData.MB_ERROR = MB_OK;
    MB_TmpData.MB_Addr = MB_ReadBuf[0];
    MB_TmpData.MB_FunCode = MB_ReadBuf[1];
    MB_TmpData.MB_RegStaAddr = (MB_ReadBuf[2] << 8) | MB_ReadBuf[3];

    if (MB_TmpData.MB_FunCode != MB_FUNCODE_WSWOUTS && MB_TmpData.MB_FunCode != MB_FUNCODE_WREGS)
    {
        MB_TmpData.CRC16 = (MB_ReadBuf[6] << 8) | MB_ReadBuf[7];
        memmove(MB_ReadBuf, MB_ReadBuf + 4, 2);
        MB_TmpData.DataAddr = (uint16_t *)MB_ReadBuf;
        *MB_TmpData.DataAddr = REG16SWAP(*MB_TmpData.DataAddr);
    }
    else
    {
        MB_TmpData.MB_WORDLEN = (MB_ReadBuf[4] << 8) | MB_ReadBuf[5];
        MB_TmpData.MB_BYTELEN = MB_ReadBuf[6];
        MB_TmpData.CRC16 = (MB_ReadBuf[MB_TmpData.MB_BYTELEN + 7] << 8) | MB_ReadBuf[MB_TmpData.MB_BYTELEN + 8];

        memmove(MB_ReadBuf, MB_ReadBuf + 7, MB_TmpData.MB_BYTELEN);
        MB_TmpData.DataAddr = (uint16_t *)&MB_ReadBuf;

        for (uint16_t i = 0; i < MB_TmpData.MB_WORDLEN; i++)
        {
            MB_TmpData.DataAddr[i] = REG16SWAP(MB_TmpData.DataAddr[i]);
        }
    }

    Modbus_DataHandler(modbus, MB_TmpData);
}

/**@description: modbus数据处理并调用用户数据回调函数
 * @param {Modbus_t} *modbus            要处理的modbus
 * @param {MODBUS_Typedef} MB_TmpData   待处理的数据
 * @return {*}
 */
static void Modbus_DataHandler(Modbus_t *modbus, MODBUS_Typedef MB_TmpData)
{
    switch (MB_TmpData.MB_FunCode)
    {
    case MB_FUNCODE_RSWIN: // 0x02
        if (modbus->MB_ReadSwitchInHandler)
        {
            MB_TmpData.MB_ERROR = modbus->MB_ReadSwitchInHandler(MB_TmpData.MB_RegStaAddr, (uint8_t *)(MB_TmpData.DataAddr + 1), *MB_TmpData.DataAddr);
        }
        else
        {
            MB_TmpData.MB_ERROR = MB_FUNCODE_ERR;
        }

        break;

    case MB_FUNCODE_RSWOUT: // 0x01
        if (modbus->MB_ReadSwitchOutHandler)
        {
            MB_TmpData.MB_ERROR = modbus->MB_ReadSwitchOutHandler(MB_TmpData.MB_RegStaAddr, (uint8_t *)(MB_TmpData.DataAddr + 1), *MB_TmpData.DataAddr);
        }
        else
        {
            MB_TmpData.MB_ERROR = MB_FUNCODE_ERR;
        }

        break;

    case MB_FUNCODE_RREG: // 0x03
        if (modbus->MB_ReadRegHandler)
        {
            MB_TmpData.MB_ERROR = modbus->MB_ReadRegHandler(MB_TmpData.MB_RegStaAddr, (MB_TmpData.DataAddr + 1), *MB_TmpData.DataAddr);
        }
        else
        {
            MB_TmpData.MB_ERROR = MB_FUNCODE_ERR;
        }

        break;

    case MB_FUNCODE_WSWOUT: // 0x05
        if (modbus->MB_WriteSwitchOutHandler)
        {
            MB_TmpData.MB_ERROR = modbus->MB_WriteSwitchOutHandler(MB_TmpData.MB_RegStaAddr, MB_TmpData.DataAddr);
        }
        else
        {
            MB_TmpData.MB_ERROR = MB_FUNCODE_ERR;
        }

        break;

    case MB_FUNCODE_WREG: // 0x06
        if (modbus->MB_WriteRegHandler)
        {
            MB_TmpData.MB_ERROR = modbus->MB_WriteRegHandler(MB_TmpData.MB_RegStaAddr, MB_TmpData.DataAddr, 1);
        }
        else
        {
            MB_TmpData.MB_ERROR = MB_FUNCODE_ERR;
        }

        break;

    case MB_FUNCODE_WREGS: // 0x10
        if (modbus->MB_WriteRegHandler)
        {
            if (MB_TmpData.MB_WORDLEN == MB_TmpData.MB_BYTELEN / 2)
            {
                MB_TmpData.MB_ERROR = modbus->MB_WriteRegHandler(MB_TmpData.MB_RegStaAddr, MB_TmpData.DataAddr, MB_TmpData.MB_WORDLEN);
            }
            else
            {
                MB_TmpData.MB_ERROR = MB_FUNCODE_ERR;
            }
        }
        else
        {
            MB_TmpData.MB_ERROR = MB_DATA_VALUE_ERR;
        }

        break;

    case MB_FUNCODE_WSWOUTS: // 0x0F
        if (modbus->MB_WriteSwitchOuts)
        {
            MB_TmpData.MB_ERROR = modbus->MB_WriteSwitchOuts(MB_TmpData.MB_RegStaAddr, (uint8_t *)MB_TmpData.DataAddr, MB_TmpData.MB_WORDLEN);
        }
        else
        {
            MB_TmpData.MB_ERROR = MB_FUNCODE_ERR;
        }

        break;

    default:
        MB_TmpData.MB_ERROR = MB_FUNCODE_ERR;
        break;
    }

    if (MB_TmpData.MB_Addr == modbus->Local_add)
    {
        Modbus_Send(modbus, MB_TmpData);
    }
}

/**@description: modbus 处理结束发送返回结果，内部调用
 * @param {Modbus_t} *modbus    //要处理的modbus
 * @param {MODBUS_Typedef} MB_TmpData   //处理的结果
 * @return {*}  无
 */
static void Modbus_Send(Modbus_t *modbus, MODBUS_Typedef MB_TmpData)
{
    uint16_t CRC16, Datalen;
    uint16_t i;

    if (MB_TmpData.MB_Addr == 0)
    {
        return;
    }

    MB_SendBuf[0] = MB_TmpData.MB_Addr;

    if (MB_TmpData.MB_ERROR)
    {
        MB_SendBuf[1] = MB_TmpData.MB_FunCode | 0x80;
        MB_SendBuf[2] = MB_TmpData.MB_ERROR;
        Datalen = 3;
    }
    else
    {
        MB_SendBuf[1] = MB_TmpData.MB_FunCode;

        switch (MB_SendBuf[1])
        {
        case MB_FUNCODE_RREG: // 0x03
            MB_SendBuf[2] = MB_TmpData.DataAddr[0] * 2;

            for (i = 0; i < MB_TmpData.DataAddr[0]; i++)
            {
                MB_SendBuf[3 + i * 2] = (MB_TmpData.DataAddr[i + 1] >> 8);
                MB_SendBuf[3 + i * 2 + 1] = (MB_TmpData.DataAddr[i + 1] & 0xff);
            }

            Datalen = 3 + (MB_TmpData.DataAddr[0]) * 2;
            break;

        case MB_FUNCODE_WSWOUT: // 0x05
        case MB_FUNCODE_WREG:   // 0x06
            MB_SendBuf[2] = MB_TmpData.MB_RegStaAddr >> 8;
            MB_SendBuf[3] = MB_TmpData.MB_RegStaAddr & 0xff;
            MB_SendBuf[4] = MB_TmpData.DataAddr[0] >> 8;
            MB_SendBuf[5] = MB_TmpData.DataAddr[0] & 0xff;
            Datalen = 6;
            break;

        case MB_FUNCODE_WSWOUTS: // 0x0F
        case MB_FUNCODE_WREGS:   // 0x10
            MB_SendBuf[2] = MB_TmpData.MB_RegStaAddr >> 8;
            MB_SendBuf[3] = MB_TmpData.MB_RegStaAddr & 0xff;
            MB_SendBuf[4] = MB_TmpData.MB_WORDLEN >> 8;
            MB_SendBuf[5] = MB_TmpData.MB_WORDLEN & 0xff;
            Datalen = 6;
            break;

        case MB_FUNCODE_RSWIN:  // 0x02
        case MB_FUNCODE_RSWOUT: // 0x02
            MB_SendBuf[2] = (MB_TmpData.DataAddr[0] + 7) / 8;

            for (i = 0; i < MB_SendBuf[2]; i++)
            {
                MB_SendBuf[i + 3] = *(((uint8_t *)(&MB_TmpData.DataAddr[1])) + i);
            }

            Datalen = 2 + MB_SendBuf[2] + 1;
            break;

        default:
            return;
        }
    }

    CRC16 = usMBCRC16(MB_SendBuf, Datalen);
    MB_SendBuf[Datalen] = CRC16 >> 8;
    MB_SendBuf[Datalen + 1] = CRC16 & 0xff;
    modbus->ModbusSendBytes(MB_SendBuf, Datalen + 2);
}

/**@description: 给modbus添加一个数据缓冲区
 * @param {Modbus_t} *modbus    要处理的modbus
 * @param {uint32_t} dataAddr   数据缓冲区地址
 * @param {uint16_t} buffsize   数据缓冲区大小
 * @return {*}
 */
void MB_AddRxDataBuf(Modbus_t *modbus, uint32_t dataAddr, uint16_t buffsize)
{
    modbus->MB_Buf.RB_Data = (uint8_t *)dataAddr;
    modbus->MB_Buf.buf_size = buffsize;
}

/**@description: 给modbus添加一个us时钟获取接口
 * @param {Modbus_t} *modbus    要处理的modbus
 * @return {*}
 */
void MB_AddGetTimestamp(Modbus_t *modbus, uint64_t (*GetTimestampus)(void))
{
    modbus->GetTimestampus = GetTimestampus;
}

/**@description: 给modbus添加一个数据发送接口，用于结果返回
 * @param {Modbus_t} *modbus    要处理的modbus
 * @param {uint8_t} *data       要发送的数据
 * @param {uint16_t} datalen    要发送的数据长度
 * @return {*}
 */
void MB_AddSendBytes(Modbus_t *modbus, void (*ModbusSendBytes)(uint8_t *data, uint16_t datalen))
{
    modbus->ModbusSendBytes = ModbusSendBytes;
}

/**@description: 设置本机地址
 * @param {Modbus_t} *modbus    要处理的modbus
 * @param {uint8_t} Addr        地址
 * @return {*}
 */
void MB_SetLocalAddr(Modbus_t *modbus, uint8_t Addr)
{
    modbus->Local_add = Addr;
}

/**@description: 设置modbus当前接口的波特率，用于确定数据帧
 * @param {Modbus_t} *modbus    要处理的modbus
 * @param {uint16_t} buad       波特率
 * @return {*}
 */
void MB_SetBuad(Modbus_t *modbus, uint32_t buad)
{
    modbus->MB_Buad = buad;
    modbus->MB_Timeout = ((uint32_t)(1000000 * 10 * 10) / buad);
}

/**@description: 设置modbus读取寄存器接口，处理数据回调函数
 * @param {Modbus_t} *modbus    要处理的modbus
 * @param {uint16_t} StartAddr  要读取的寄存器地址
 * @param {uint16_t} *data      存放要读取的数据
 * @param {uint16_t} Datalen    要读取的寄存器长度
 * @return {*}
 */
void MB_SetReadRegs_UserFun(Modbus_t *modbus, MB_ERR_Typedef (*MB_ReadRegHandler)(uint16_t StartAddr, uint16_t *data, uint16_t Datalen))
{
    modbus->MB_ReadRegHandler = MB_ReadRegHandler;
}

/**@description: 设置modbus写入寄存器接口，处理数据回调函数
 * @param {Modbus_t} *modbus    要处理的modbus
 * @param {uint16_t} StartAddr  要写入的寄存器地址
 * @param {uint16_t} *data      要写入的数据
 * @param {uint16_t} Datalen    要写入的寄存器长度
 * @return {*}
 */
void MB_SetWriteRegs_UserFun(Modbus_t *modbus, MB_ERR_Typedef (*MB_WriteRegHandler)(uint16_t StartAddr, uint16_t *data, uint16_t Datalen))
{
    modbus->MB_WriteRegHandler = MB_WriteRegHandler;
}

/**@description: 设置modbus读取离散输入接口，处理数据回调函数
 * @param {Modbus_t} *modbus    要处理的modbus
 * @param {uint16_t} StartAddr  要读取的离散输入地址
 * @param {uint8_t} *data       存放要读取的数据    //1bit 一个输入状态
 * @param {uint16_t} Datalen    要读取的离散输入个数
 * @return {*}
 */
void MB_SetReadSWIN_UserFun(Modbus_t *modbus, MB_ERR_Typedef (*MB_ReadSwitchInHandler)(uint16_t StartAddr, uint8_t *data, uint16_t Datalen))
{
    modbus->MB_ReadSwitchInHandler = MB_ReadSwitchInHandler;
}

/**@description: 设置modbus读取线圈输出接口，处理数据回调函数
 * @param {Modbus_t} *modbus    要处理的modbus
 * @param {uint16_t} StartAddr  要读取线圈的寄存器地址
 * @param {uint8_t} *data       存放要读取的数据    //1bit 一个线圈状态
 * @param {uint16_t} Datalen    要读取的线圈个数
 * @return {*}
 */
void MB_SetReadSWOUT_UserFun(Modbus_t *modbus, MB_ERR_Typedef (*MB_ReadSwitchOutHandler)(uint16_t StartAddr, uint8_t *data, uint16_t Datalen))
{
    modbus->MB_ReadSwitchOutHandler = MB_ReadSwitchOutHandler;
}

/**@description: 设置modbus单个线圈输出接口，处理数据回调函数
 * @param {Modbus_t} *modbus    要处理的modbus
 * @param {uint16_t} StartAddr  要写入线圈的寄存器地址
 * @param {uint8_t} *data       //0x00FF 分离线圈 //0xFF00 合上线圈
 * @return {*}
 */
void MB_SetWriteSWOUT_UserFun(Modbus_t *modbus, MB_ERR_Typedef (*MB_WriteSwitchOutHandler)(uint16_t StartAddr, uint16_t *data))
{
    modbus->MB_WriteSwitchOutHandler = MB_WriteSwitchOutHandler;
}

/**@description: 设置modbus多个线圈输出接口，处理数据回调函数
 * @param {Modbus_t} *modbus    要处理的modbus
 * @param {uint16_t} StartAddr  要写入线圈的寄存器地址
 * @param {uint8_t} *data       要写入线圈的数据    //1bit 一个线圈状态
 * @param {uint16_t} OutNum     要写入线圈的个数
 * @return {*}
 */
void MB_SetWriteSWOUTS_UserFun(Modbus_t *modbus, MB_ERR_Typedef (*MB_WriteSwitchOuts)(uint16_t StartAddr, uint8_t *data, uint16_t OutNum))
{
    modbus->MB_WriteSwitchOuts = MB_WriteSwitchOuts;
}
